// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;

namespace SnapObjects.Expressions
{
    internal class DefaultEvaluatePropertyProvider : MetadataProvider<EvaluateResult>
    {
        private readonly CacheCollection<Type, IDictionary<string, PropertyInfo>> _types;

        public DefaultEvaluatePropertyProvider(IServiceProvider services)
        {
            _types = new CacheCollection<Type, IDictionary<string, PropertyInfo>>(
                services.GetService<ICacheProvider>(), CacheSourceKind.EVALUATE_PROPERTY, x => x.FullName);
        }

        public override MetadataType Type => MetadataType.Property;

        public override bool TryGet(
            object key, ParserOptions<EvaluateResult> options, out Metadata<EvaluateResult> metadata)
        {
            var name = (string)key;

            if (String.IsNullOrWhiteSpace(name))
            {
                throw new ArgumentNullException(nameof(name));
            }

            var type = options.DataSource.GetType();

            var properties = _types.GetOrSet(type, () => this.GetProperties(type));

            if (properties.TryGetValue(name, out var property))
            {
                var value = new EvaluateResult(
                    property.GetValue(options.DataSource),
                    property.PropertyType);

                metadata = new PropertyMetadata<EvaluateResult>(
                    property.Name, value);

                return true;
            }

            metadata = null;

            return false;
        }

        private IDictionary<string, PropertyInfo> GetProperties(Type type)
        {
            return type.GetProperties().ToDictionary(
                x => x.Name, x => x, StringComparer.OrdinalIgnoreCase);

        }
    }
}
